#ifndef RENDERER_HPP
#define RENDERER_HPP

#include <vector>
#include <cmath>
#include <iostream>

#include "Eigen"
#if !defined(STB_IMAGE_IMPLEMENTATION)
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#endif // STB_IMAGE_IMPLEMENTATION
#if !defined(STB_IMAGE_WRITE_IMPLEMENTATION)
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"
#endif // STB_IMAGE_WRITE_IMPLEMENTATION

#include "Ray.hpp"
#include "Sphere.hpp"
#include "utilities.hpp"
#include "ObjectList.hpp"
#include "Camera.hpp"
#include "ThreadPool.hpp"

class Renderer {
private:
    // 渲染图象的宽
    size_t w;
    // 渲染图像的高
    size_t h;
    // 渲染图像数据
    std::vector<std::byte> frameBuffer;
    // 相机
    Camera camera;
    // 随机采样反走样，每个像素随机采样次数
    size_t samplesPerPixel = 20;
    // 光线追踪递归次数
    size_t depth = 5;

public:
    Renderer(size_t width, size_t height, size_t samplesPerPixel = 20)
        : w(width), h(height),
        frameBuffer(w* h * 3, std::byte(0)),
        camera(16.f / 9.f),
        samplesPerPixel(samplesPerPixel)
        // pool(6)
    {}
    // 渲染出图
    void render(const ObjectList& scene);
    // 渲染一定范围的像素行
    void render_rows(
        const ObjectList& scene,
        int rowBegin, int rowEnd);
    // 多线程加速渲染
    void multi_render(const ObjectList& scene, int rows = 10);

    int save_png(const std::string& filename)const {
        std::cout << "Save" << std::endl;
        return stbi_write_png(filename.c_str(), w, h, 3, frameBuffer.data(), 0);
    }
    void setDepth(size_t d) { depth = d; }
    void setCamera(const Camera& camera) {
        this->camera = camera;
    }

    ~Renderer() {
        frameBuffer.~vector();
    }

private:
    void write_color(int row, int col, const Eigen::Vector3f& rgb);
};

void Renderer::render(const ObjectList& scene) {
    int count = 0;
    for (size_t i = 0; i < w; ++i) {
        for (size_t j = h - 1; j > 0; --j) {
            Eigen::Vector3f color(0, 0, 0);
            // 均匀采样
            // for (int si = 0; si < samplesPerPixel;++si) {
            //     for (int sj = 0; sj < samplesPerPixel;++sj) {
            //         Ray r = camera.get_ray((i + (float)si / samplesPerPixel) / (w - 1), (j + (float)sj / samplesPerPixel)/(h - 1));
            //         color += cast_ray(r);
            //     }
            // }
            // 随机采样
            for (int s = 0; s < samplesPerPixel; ++s) {
                float u = (i + rand_float()) / (w - 1);
                float v = (j + rand_float()) / (h - 1);
                color += cast_ray(camera.get_ray(u, v), scene, depth);
            }
            color /= samplesPerPixel;
            write_color(h - j - 1, i, color);
        }
        if (i - count > w / 20) {
            count = i;
            std::cout << "\b=>";
        }
    }
}

void write_color(int row, int col, const Eigen::Vector3f& rgb, std::vector<std::byte>& frameBuffer, int w) {
    frameBuffer[(row * w + col) * 3] = static_cast<std::byte>(255.f * clamp(rgb.x(), 0, 1));
    frameBuffer[(row * w + col) * 3 + 1] = static_cast<std::byte>(255.f * clamp(rgb.y(), 0, 1));
    frameBuffer[(row * w + col) * 3 + 2] = static_cast<std::byte>(255.f * clamp(rgb.z(), 0, 1));
    printf("%d %d %d\n", frameBuffer[(row * w + col) * 3], frameBuffer[(row * w + col) * 3 + 1], frameBuffer[(row * w + col) * 3 + 2]);
}
// 渲染一定范围的像素行
void Renderer::render_rows(
    const ObjectList& scene,
    int rowBegin, int rowEnd)
{
    for (int i = rowBegin; i < rowEnd;++i) {
        for (int j = 0; j < w;++j) {
            Eigen::Vector3f color(0, 0, 0);
            for (int s = 0; s < samplesPerPixel; ++s) {
                float u = (j + rand_float()) / (w - 1);
                float v = (i + rand_float()) / (h - 1);
                color += cast_ray(camera.get_ray(u, v), scene, depth);
            }
            color /= samplesPerPixel;
            color << sqrtf(color.x()), sqrtf(color.y()), sqrtf(color.z());
            write_color(h - i - 1, j, color);
        }
    }
    printf("\b=>");
}
// 多线程加速渲染
void Renderer::multi_render(const ObjectList& scene, int rows) {
    ThreadPool pool(6);
    int i;
    for (i = 0; i < h; i += rows) {
        pool.enqueue(&Renderer::render_rows, this, scene, i, i + rows);
    }
    if (i < h) {
        pool.enqueue(&Renderer::render_rows, this, scene, i, h);
    }
}
void Renderer::write_color(int row, int col, const Eigen::Vector3f& rgb) {
    frameBuffer[(row * w + col) * 3] = static_cast<std::byte>(255.f * clamp(rgb.x(), 0, 1));
    frameBuffer[(row * w + col) * 3 + 1] = static_cast<std::byte>(255.f * clamp(rgb.y(), 0, 1));
    frameBuffer[(row * w + col) * 3 + 2] = static_cast<std::byte>(255.f * clamp(rgb.z(), 0, 1));
}
#endif